﻿using System.Security.Cryptography;
using System;

namespace Framework.Crypto.Ciphers
{
    /// <summary>
    /// Generic cipher algorythm abstraction.
    /// Implements ICryptoProvider.
    /// </summary>
    public abstract class ICipher : ICryptoProvider // We'll also create IDisposable inheritance later here or to childs.
    {
        /// <summary>
        /// Content to be encoded or decoded
        /// </summary>
        protected byte[] data;

        /// <summary>
        /// Gets the content to be encoded or decoded
        /// </summary>
        public byte[] Data { get { return data; } }

        /// <summary>
        /// Key for encoding or decoding
        /// </summary>
        protected byte[] key;

        /// <summary>
        /// Gets the key for encoding or decoding
        /// </summary>
        public byte[] Key { get { return key; } }

        /// <summary>
        /// Basic constructor for usage without key generation.
        /// </summary>
        /// <param name="data"></param>
        /// <param name="key"></param>
        public ICipher(byte[] data, byte[] key)
        {
            if (key == null)
            {
                throw new ArgumentNullException("key", "You're trying to use null byte[] array as a key.");
            }

            this.data = data;
            this.key = key;
        }

        /// <summary>
        /// Basic constructor for usage with key generation.
        /// New key can be accessed in any instance by Key field.
        /// </summary>
        /// <param name="data"></param>
        /// <param name="keyLength"></param>
        public ICipher(byte[] data, int keyLength)
        {
            this.data = data;
            Keygen(keyLength);
        }

        /// <summary>
        /// Appends byte[]-based content to buffer.
        /// </summary>
        /// <param name="appendix"></param>
        public void Append(byte[] appendix)
        {
            if (appendix == null)
            {
                throw new ArgumentNullException("appendix", "You're trying to append null byte[] array.");
            }

            int newSize = data.Length + appendix.Length;
            byte[] newData = new byte[newSize];
            Array.Copy(data, newData, data.Length);
            Array.Copy(appendix, 0, newData, data.Length, appendix.Length);
            data = newData;
        }

        /// <summary>
        /// Keygen algorythm
        /// </summary>
        public virtual void Keygen(int keyLength)
        {
            byte[] newKey = new byte[keyLength];
            //RNGCryptoServiceProvider is an implementation of a random number generator.
            RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
            // The array is now filled with cryptographically strong random bytes.
            rng.GetBytes(newKey);
            key = newKey;
        }

        /// <summary>
        /// Byte swapping
        /// </summary>
        /// <param name="b1">first byte</param>
        /// <param name="b2">second byte</param>
        protected virtual void Swap(ref byte b1, ref byte b2)
        {
            byte t;
            t = b1;
            b1 = b2;
            b2 = t;
        }

        /// <summary>
        /// Start encryption
        /// </summary>
        /// <returns></returns>
        public abstract byte[] Encrypt();

        /// <summary>
        /// Start decryption
        /// </summary>
        /// <returns></returns>
        public abstract byte[] Decrypt();

        /// <summary>
        /// Clearance of byte buffer.
        /// </summary>
        public abstract void Clear();
    }
}
